我有分寸

[译文]strace:系统调用跟踪与信号报告工具

gnawux stracetoolstranslation

原文出处:http://linuxgazette.net/148/saha.html
原文作者:Amit Kumar Saha and Sayantini Ghosh
翻译时间:2008年3月3日
译者:王旭 <gnawux (at) gmail.com>

译者按:翻译篇文章换换心情,strace 是个有用的工具,LinuxGazette 是个不错的杂志,本文也希望你喜欢。

了解一切是怎么运转的是件有趣的事情。所有的C程序员们都知道,在他们的C程序的“输入-处理-输出”周期中用到了很多系统调用 。 看看程序中到底哪些系统调用被用到了无疑是件很让人兴奋的事情。本文就是关于这个话题的,让我们开始吧。

何为 ’strace’?

’strace’ 是一个用于在运行时跟踪进程调用的系统调用的工具。它同时报告进程收到的信号(或软中断) 。

根据手册页,在最简单的情况下,“strace 运行指定的命令直到该命令执行完成。它截获并记录进程调用的系统调用和收到的信号。”

直接在终端中敲入”strace”命令就可以看到它的各个开关和选项:

$ strace
usage: strace [-dffhiqrtttTvVxx] [-a column] [-e expr] ... [-o file]
[-p pid] ... [-s strsize] [-u username] [-E var=val] ...
[command [arg ...]]
or: strace -c [-e expr] ... [-O overhead] [-S sortby] [-E var=val] ...
[command [arg ...]]
-c -- count time, calls, and errors for each syscall and report summary

[[[etc.]]]
跟踪系统调用

先从一个简单的例子开始。考虑如下C代码(清单1):

/* Listing 1*/

#include <stdio.h>

int main()
{
return 0;
}

假设编译好的目标文件名为’temp.o’。如下运行:

$strace ./temp.o

将会得到如下跟踪结果输出:

execve("./temp.o", ["./temp.o"], [/* 36 vars */]) = 0
brk(0) = 0x804a000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fba000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=68539, ...}) = 0
mmap2(NULL, 68539, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fa9000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/tls/i686/cmov/libc.so.6", O_RDONLY) = 3
read(3, "177ELF111331`100"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0644, st_size=1307104, ...}) = 0
mmap2(NULL, 1312164, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7e68000
mmap2(0xb7fa3000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13b) = 0xb7fa3000
mmap2(0xb7fa6000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fa6000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7e67000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7e676c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7fa3000, 4096, PROT_READ) = 0
munmap(0xb7fa9000, 68539) = 0
exit_group(0) = ?
Process 8909 detached

现在,我们来理论联系实际一下。

我们知道,当用户输入命令或可执行文件来运行的时候,系统会制造一个“子”Shell,并用这个子Shell来运行程序。这是通过系统调用”execve”来实现的。因此,跟踪结果是以如下内容开始的:

execve("./temp.o", ["./temp.o"], [/* 36 vars */]) = 0

接下来,进程调用 ‘brk()’, ‘open’,'access’, ‘open’, ‘close’ 知道最终进程从 shell 中分离并使用”exit_group(0)”推出。

如上,跟踪过程显示了系统调用及其返回值。

strace的信号报告功能

下面来看一下”strace”的信号报告功能。考虑如下C代码(列表2):

/*Listing 2*/

#include <stdio.h>

int main()
{
int i;

for(i=0;i>=0;i++)
printf("infinityn");
return 0;
}

假设编译输出的可执行文件为”temp-1.o”。如下运行:

$ strace -o trace.txt ./temp-1.o

这里,”-o”开关将把跟踪结果保存到”trace.txt”文件。

这里,你将会看到”write()”系统调用将不停地被调用,现在,用”ctrl-c”来结束这个进程

[[[...]]]

write(1, "tyninfinityninfinityninfinitynin"..., 1024) = 1024
write(1, "nityninfinityninfinityninfinityn"..., 1024) = 1024
write(1, "finityninfinityninfinityninfinit"..., 1024) = 1024
write(1, "infinityninfinityninfinityninfin"..., 1024) = 1024
write(1, "yninfinityninfinityninfinityninf"..., 1024) = 1024
write(1, "ityninfinityninfinityninfinityni"..., 1024) = 1024
write(1, "inityninfinityninfinityninfinity"..., 1024) = 1024
write(1, "nfinityninfinityninfinityninfini"..., 1024) = 1024
write(1, "ninfinityninfinityninfinityninfi"..., 1024) = 1024
write(1, "tyninfinityninfinityninfinitynin"..., 1024) = 1024
write(1, "nityninfinityninfinityninfinityn"..., 1024) = 1024
write(1, "finityninfinityninfinityninfinit"..., 1024) = 1024
write(1, "infinityninfinityninfinityninfin"..., 1024) = 1024
write(1, "yninfinityninfinityninfinityninf"..., 1024) = 1024

[[[etc.]]]

现在,查看”trace.txt”

$cat trace.txt

其中的最后几行应该是:

--- SIGINT (Interrupt) @ 0 (0) ---
+++ killed by SIGINT +++

因为我们使用”ctrl-c”结束了进程,于是信号 SIGINT 被发送给进程 ,正如”strace”所输出的一样。

收集系统调用相关的统计信息

使用”strace”,还可以对跟踪的系统调用进行一些简单的统计。这通过”-c”开关实现。例如:

$ strace -o trace-1.txt -c ./temp-1.o # 运行上述可执行程序 'temp-1.o'
$ cat trace-1.txt

% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.007518 0 46702 write
0.00 0.000000 0 1 read
0.00 0.000000 0 2 open
0.00 0.000000 0 2 close
0.00 0.000000 0 1 execve
0.00 0.000000 0 3 3 access
0.00 0.000000 0 1 brk
0.00 0.000000 0 1 munmap
0.00 0.000000 0 1 mprotect
0.00 0.000000 0 7 mmap2
0.00 0.000000 0 3 fstat64
0.00 0.000000 0 1 set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00 0.007518 46725 3 total

如上,在其他输出信息之外,同时还输出了系统调用的统计信息,”write()”系统调用(一共运行了46702次),花去了进程的绝大部分时间(100%)。

后记

本文对”strace”的一些基本功能进行了简单的介绍。这个工具在进行可执行程序的bug寻找、寻找程序崩溃点的时候非常有用。使用 “strace”,可以极大地缩小问题可能发生的范围。

‘GNU Debugger’ (gdb)‘ltrace’一起,”strace” 为 Linux 程序员提供了强大的调试能力。

有用链接:
gnawux
me!#$!@#$@#$wangxu!@#$%^&*()_me